package com.zyy.game.tianlong.network;

import com.zyy.game.tianlong.network.callback.GlobalExceptionCallBack;
import com.zyy.game.tianlong.network.codec.GameByteToMsgDecoder;
import com.zyy.game.tianlong.network.codec.GameMsgToByteEncoder;
import com.zyy.game.tianlong.network.config.NettyConfig;
import com.zyy.game.tianlong.network.exception.ThreadRejectedException;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteOrder;
import java.util.concurrent.TimeUnit;

/**
 * LengthField ChannelInitializer
 *
 * @author relufi
 * @date 2019/7/27 0027
 */
@ChannelHandler.Sharable
@Slf4j
public class GameLengthFieldChannelInitializer extends ChannelInitializer<NioSocketChannel> {
    private final GlobalExceptionCallBack globalExceptionCallBack;
    private final NettyConfig nettyConfig;
    private final DefaultEventExecutorGroup businessGroup;
    private final GameDispatcher gameMsgHandler;

    private GameMsgToByteEncoder gameMsgToByteEncoder = new GameMsgToByteEncoder();
    private GameByteToMsgDecoder gameByteToMsgDecoder;
    private ShowHexDump showHexDump = new ShowHexDump();

    public GameLengthFieldChannelInitializer(GlobalExceptionCallBack globalExceptionCallBack, NettyConfig nettyConfig, DefaultEventExecutorGroup businessGroup, GameDispatcher gameMsgHandler) {
        this.globalExceptionCallBack = globalExceptionCallBack;
        this.nettyConfig = nettyConfig;
        this.businessGroup = businessGroup;
        this.gameMsgHandler = gameMsgHandler;
        gameByteToMsgDecoder = new GameByteToMsgDecoder(globalExceptionCallBack);
    }

    @Override
    protected void initChannel(NioSocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        if (nettyConfig.isShowHexDump()) {
            pipeline.addLast("showHexDump", showHexDump);
        }
        pipeline
                .addLast("gameEncoder", gameMsgToByteEncoder)
                .addLast("lengthDecoder", new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1048576, 0, 4, 0, 4, true))
                .addLast("gameDecoder", gameByteToMsgDecoder);
        if(nettyConfig.isHeartBeat()) {
            pipeline.addLast("heartBeat", new IdleStateHandler(6, 0, 0, TimeUnit.SECONDS))
                    .addLast("heartBeatHandler", new HeartBeatHandler());
        }
        pipeline.addLast(businessGroup, "gameHandler", gameMsgHandler);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (cause instanceof ThreadRejectedException) {
            globalExceptionCallBack.exceptionCaught(new GameContext(ctx), cause);
        } else {
            super.exceptionCaught(ctx, cause);
        }
    }
}
